﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Hook.Graphics;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Storage;
using System.IO;
using Hook.Environmental.Props.EnvironmentPieces;
using Hook.Primitives;
using Hook.Environmental.Props;
using Hook.Environmental.Editors;
using Hook.Environmental.Props.EnvironmentPieces.ForestPieces;
using Hook.Environmental.Props.Enemies;

namespace Hook.Environmental
{
    public class LevelEditor
    {
        public const int GRIDSPACING = 50;
        public const int NUMINPALETTE = 6;

        public IAsyncResult SyncResult;
        public bool GridSizeKeyed, LeftMouseDown, RightMouseDown, MiddleMouseDown, PageUpDown, PageDownDown, PointBeingDragged, LevelSaveRequested, LevelLoadRequested;
        public bool RightKeyDown, DownKeyDown, LeftKeyDown, UpKeyDown, QKeyDown, EKeyDown, TabKeyDown, LevelNameSelected;
        public int PrevMouseScroll, CurrentMouseScroll;
        public bool[] NumberKeyDown = new bool[10];
        //public string LevelName;

        public Vector3 MousePosition;

        public Camera Cam;
        public Level CurrentLevel;

        public PropPalette PropPalette;
        //public List<Prop> PropPalette;
        //public int PropSelection;
        public int PropSet, PropPiece;
        public Prop PlacementProp;

        public TextField LevelNameField;
        
        public LevelEditor()
        {
            Cam = new Camera();
            CurrentLevel = new Level();
            //LevelName = "TestLevel.txt";

            LevelNameField = new TextField(new Vector3(-Game1.RESOLUTION.X / 2 + 10, -Game1.RESOLUTION.Y / 2 + 5, 0));
            LevelNameField.Text = "Untitled.txt";

            MousePosition = Vector3.Zero;
            CurrentMouseScroll = PrevMouseScroll = Mouse.GetState().ScrollWheelValue;

            PropPalette = new PropPalette();

            /*PropPalette = new List<Prop>();
            PropPalette.Add(new LargeRectangle());
            PropPalette.Add(new LargeTriangle());
            PropPalette.Add(new SmallIsoTriangle());
            PropPalette.Add(new SmallRightTriangle());
            PropPalette.Add(new SmallSquare());
            PropPalette.Add(new TinySquare());
            PropSelection = 0;*/

            //PlacementProp = new LargeRectangle();
            PropSet = PropPiece = 0;
            PlacementProp = PropPalette.SwabPropFromPalette(PropSet, PropPiece);
            //SetPlacementProp(PropSelection);
        }

        public void HandleInput()
        {
            KeyboardState kbs = Keyboard.GetState();
            MouseState ms = Mouse.GetState();
            MousePosition = new Vector3(ms.X - Game1.RESOLUTION.X / 2, ms.Y - Game1.RESOLUTION.Y / 2, 0) + Cam.Position;
            CurrentMouseScroll = ms.ScrollWheelValue;

            if (ms.LeftButton == ButtonState.Pressed && !LeftMouseDown)
            {
                if (MouseOverName())
                {
                    LevelNameField.Selected = true;
                }
                else
                {
                    LevelNameField.Selected = false;
                    PlacementProp.SetPosition(AlignPositionToGrid(MousePosition));
                    CurrentLevel.Props.Add(PlacementProp);

                    if (PlacementProp.Name.Equals("LevelStart.txt"))
                    {
                        CurrentLevel.LevelStart = PlacementProp;
                    }
                    else if (PlacementProp.Name.Equals("LevelFinish.txt"))
                    {
                        CurrentLevel.LevelFinish = PlacementProp;
                    }

                    PlacementProp = PropPalette.SwabPropFromPalette(PropSet, PropPiece);
                }
                
                LeftMouseDown = true;
            }
            if (ms.LeftButton == ButtonState.Released)
            {
                LeftMouseDown = false;
            }

            if (ms.RightButton == ButtonState.Pressed && !RightMouseDown)
            {
                int TargetIndex = FindTopPropIndexAtPoint(MousePosition);
                if (TargetIndex != -1)
                {
                    CurrentLevel.Props.RemoveAt(TargetIndex);
                }
                RightMouseDown = true;
            }
            if (ms.RightButton == ButtonState.Released)
            {
                RightMouseDown = false;
            }

            if (ms.MiddleButton == ButtonState.Pressed && !MiddleMouseDown)
            {
                MiddleMouseDown = true;
            }
            if (ms.MiddleButton == ButtonState.Released)
            {
                MiddleMouseDown = false;
            }
            if (MiddleMouseDown)
            {
                Cam.Position = MousePosition;
            }

            int MouseScrollDifference = CurrentMouseScroll - PrevMouseScroll;
            if (MouseScrollDifference != 0)
            {
                RotateProp(new Vector3(0, 0, MouseScrollDifference * (float)Math.PI / 32));
            }
            PrevMouseScroll = CurrentMouseScroll;

            if (kbs.IsKeyDown(Keys.PageUp) && !PageUpDown)
            {
                RotateProp(new Vector3(0, 0, -(float)Math.PI / 4));
                PageUpDown = true;
            }
            if (kbs.IsKeyDown(Keys.PageDown) && !PageDownDown)
            {
                RotateProp(new Vector3(0, 0, (float)Math.PI / 4));
                PageDownDown = true;
            }
            if (kbs.IsKeyUp(Keys.PageUp))
            {
                PageUpDown = false;
            }
            if (kbs.IsKeyUp(Keys.PageDown))
            {
                PageDownDown = false;
            }

            if (kbs.IsKeyDown(Keys.E) && !EKeyDown)
            {
                RollPiece(true);
                PlacementProp = PropPalette.SwabPropFromPalette(PropSet, PropPiece);
                EKeyDown = true;
            }
            if (kbs.IsKeyDown(Keys.Q) && !QKeyDown)
            {
                RollPiece(false);
                PlacementProp = PropPalette.SwabPropFromPalette(PropSet, PropPiece);
                QKeyDown = true;
            }
            if (kbs.IsKeyDown(Keys.Tab) && !TabKeyDown)
            {
                if (kbs.IsKeyDown(Keys.LeftShift))
                {
                    RollSet(false);
                }
                else
                {
                    RollSet(true);
                }

                PlacementProp = PropPalette.SwabPropFromPalette(PropSet, PropPiece);
                TabKeyDown = true;
            }

            if (kbs.IsKeyUp(Keys.E))
            {
                EKeyDown = false;
            }
            if (kbs.IsKeyUp(Keys.Q))
            {
                QKeyDown = false;
            }
            if (kbs.IsKeyUp(Keys.Tab))
            {
                TabKeyDown = false;
            }

            /*if (kbs.IsKeyDown(Keys.D0) && !NumberKeyDown[0])
            {
                SetPlacementProp(PropSelection = 0);
                NumberKeyDown[0] = true;
            }
            if (kbs.IsKeyDown(Keys.D1) && !NumberKeyDown[1])
            {
                SetPlacementProp(PropSelection = 1);
                NumberKeyDown[1] = true;
            }
            if (kbs.IsKeyDown(Keys.D2) && !NumberKeyDown[2])
            {
                SetPlacementProp(PropSelection = 2);
                NumberKeyDown[2] = true;
            }
            if (kbs.IsKeyDown(Keys.D3) && !NumberKeyDown[3])
            {
                SetPlacementProp(PropSelection = 3);
                NumberKeyDown[3] = true;
            }
            if (kbs.IsKeyDown(Keys.D4) && !NumberKeyDown[4])
            {
                SetPlacementProp(PropSelection = 4);
                NumberKeyDown[4] = true;
            }
            if (kbs.IsKeyDown(Keys.D5) && !NumberKeyDown[5])
            {
                SetPlacementProp(PropSelection = 5);
                NumberKeyDown[5] = true;
            }*/
            if (kbs.IsKeyUp(Keys.D0))
            {
                NumberKeyDown[0] = false;
            }
            if (kbs.IsKeyUp(Keys.D1))
            {
                NumberKeyDown[1] = false;
            }
            if (kbs.IsKeyUp(Keys.D2))
            {
                NumberKeyDown[2] = false;
            }
            if (kbs.IsKeyUp(Keys.D3))
            {
                NumberKeyDown[3] = false;
            }
            if (kbs.IsKeyUp(Keys.D4))
            {
                NumberKeyDown[4] = false;
            }
            if (kbs.IsKeyUp(Keys.D5))
            {
                NumberKeyDown[5] = false;
            }

            if (kbs.IsKeyDown(Keys.Right) || kbs.IsKeyDown(Keys.D))
            {
                Cam.Position.X += 15;
            }
            if (kbs.IsKeyDown(Keys.Down) || kbs.IsKeyDown(Keys.S))
            {
                Cam.Position.Y += 15;
            }
            if (kbs.IsKeyDown(Keys.Left) || kbs.IsKeyDown(Keys.A))
            {
                Cam.Position.X -= 15;
            }
            if (kbs.IsKeyDown(Keys.Up) || kbs.IsKeyDown(Keys.W))
            {
                Cam.Position.Y -= 15;
            }

            if (kbs.IsKeyUp(Keys.Right))
            {
                RightKeyDown = false;
            }
            if (kbs.IsKeyUp(Keys.Down))
            {
                DownKeyDown = false;
            }
            if (kbs.IsKeyUp(Keys.Left))
            {
                LeftKeyDown = false;
            }
            if (kbs.IsKeyUp(Keys.Up))
            {
                UpKeyDown = false;
            }

            if (kbs.IsKeyDown(Keys.OemMinus) && !LevelSaveRequested)
            {
                // Set the request flag
                if (!Guide.IsVisible)
                {
                    LevelSaveRequested = true;
                    SyncResult = Guide.BeginShowStorageDeviceSelector(PlayerIndex.One, null, null);
                }
            }
            if (kbs.IsKeyDown(Keys.OemPlus) && !LevelLoadRequested)
            {
                //LoadPropData("PropSaves/TestProp.txt");

                // Set the request flag
                if (!Guide.IsVisible)
                {
                    LevelLoadRequested = true;
                    SyncResult = Guide.BeginShowStorageDeviceSelector(PlayerIndex.One, null, null);
                }
            }

            PlacementProp.SetPosition(AlignPositionToGrid(MousePosition));
        }

        public void Update(GameTime gt)
        {
            HandleInput();

            LevelNameField.Update(gt);

            CurrentLevel.Update(gt);
            PlacementProp.Update(gt);

            if ((LevelSaveRequested) && (SyncResult.IsCompleted))
            {
                StorageDevice Device = Guide.EndShowStorageDeviceSelector(SyncResult);
                if (Device != null && Device.IsConnected)
                {
                    SaveLevelData(Device, LevelNameField.Text);
                }
                // Reset the request flag
                LevelSaveRequested = false;
            }

            if ((LevelLoadRequested) && (SyncResult.IsCompleted))
            {
                StorageDevice Device = Guide.EndShowStorageDeviceSelector(SyncResult);
                if (Device != null && Device.IsConnected)
                {
                    LoadLevelData(Device, LevelNameField.Text);
                }
                // Reset the request flag
                LevelLoadRequested = false;
            }
        }

        public void RollSet(bool Direction)
        {
            if (Direction)
            {
                PropSet++;
                if (PropSet >= PropPalette.Palette.Length)
                {
                    PropSet = 0;
                }
            }
            else
            {
                PropSet--;
                if (PropSet < 0)
                {
                    PropSet = PropPalette.Palette.Length - 1;
                }
            }
            PropPiece = 0;
        }
        public void RollPiece(bool Direction)
        {
            if (Direction)
            {
                PropPiece++;
                if (PropPiece >= PropPalette.Palette[PropSet].Count)
                {
                    PropPiece = 0;
                }
            }
            else
            {
                PropPiece--;
                if (PropPiece < 0)
                {
                    PropPiece = PropPalette.Palette[PropSet].Count - 1;
                }
            }
        }

        public void RotateProp(Vector3 Rotation)
        {
            PlacementProp.SetRotation(PlacementProp.Rotation + Rotation);
            //PlacementProp.SetRotation(new Vector3(0, 0, PlacementProp.Rotation.Z % (2 * (float)Math.PI)));
        }

        public void Draw(SpriteBatch sb)
        {
            CurrentLevel.Draw(sb, Cam);
            PlacementProp.Draw(sb, Cam);

            LevelNameField.Draw(sb);
        }

        public void SetPlacementProp(int Selection)
        {
            switch (Selection)
            {
                case 0: PlacementProp = new LargeRectangle(MousePosition, PlacementProp.Rotation);      break;
                case 1: PlacementProp = new LargeTriangle(MousePosition, PlacementProp.Rotation);       break;
                case 2: PlacementProp = new SmallIsoTriangle(MousePosition, PlacementProp.Rotation);    break;
                case 3: PlacementProp = new SmallRightTriangle(MousePosition, PlacementProp.Rotation);  break;
                case 4: PlacementProp = new SmallSquare(MousePosition, PlacementProp.Rotation);         break;
                case 5: PlacementProp = new TinySquare(MousePosition, PlacementProp.Rotation);          break;
            }
        }

        public Vector3 AlignPositionToGrid(Vector3 Position)
        {
            Vector3 AlignedPosition = new Vector3((float)Math.Round(Position.X / GRIDSPACING) * GRIDSPACING, (float)Math.Round(Position.Y / GRIDSPACING) * GRIDSPACING, (float)Math.Round(Position.Z / GRIDSPACING) * GRIDSPACING);
            return AlignedPosition;
        }

        public int FindTopPropIndexAtPoint(Vector3 Target)
        {
            for (int p = CurrentLevel.Props.Count - 1; p >= 0; p--)
            {
                if (CurrentLevel.Props[p].PointCollides(new PolyPoint(Target)))
                {
                    return p;
                }
            }
            return -1;
        }

        public void LoadLevelData(StorageDevice Device, string FileName)
        {
            StorageContainer Container = Device.OpenContainer("Levels");

            // Get the path of the save game.
            FileName = "Levels/" + FileName;
            //FileName = Path.Combine(Container.Path, FileName);

            if (File.Exists(FileName))
            {
                List<string> Lines = new List<string>();
                StreamReader SR = new StreamReader(FileName);

                string CurrentLine = SR.ReadLine();
                while (CurrentLine != null)
                {
                    Lines.Add(CurrentLine);
                    CurrentLine = SR.ReadLine();
                }

                for (int s = 0; s < Lines.Count; s++)
                {
                    if (Lines[s].StartsWith("LevelMusic = "))
                    {
                        CurrentLevel.LevelMusic = Lines[s].Substring("LevelMusic = ".Length);
                    }
                    else if (Lines[s].StartsWith("LevelStart = "))
                    {
                        Vector3 PositionVector = new Vector3();
                        int XLoc, YLoc, ZLoc;
                        XLoc = Lines[s + 1].IndexOf('X');
                        YLoc = Lines[s + 1].IndexOf('Y');
                        ZLoc = Lines[s + 1].IndexOf('Z');
                        PositionVector.X = float.Parse(Lines[s + 1].Substring(XLoc + 2, YLoc - XLoc - 3));
                        PositionVector.Y = float.Parse(Lines[s + 1].Substring(YLoc + 2, ZLoc - YLoc - 3));
                        PositionVector.Z = float.Parse(Lines[s + 1].Substring(ZLoc + 2, Lines[s + 1].Length - ZLoc - 3));

                        Vector3 RotationVector = new Vector3();
                        XLoc = Lines[s + 2].IndexOf('X');
                        YLoc = Lines[s + 2].IndexOf('Y');
                        ZLoc = Lines[s + 2].IndexOf('Z');
                        RotationVector.X = float.Parse(Lines[s + 2].Substring(XLoc + 2, YLoc - XLoc - 3));
                        RotationVector.Y = float.Parse(Lines[s + 2].Substring(YLoc + 2, ZLoc - YLoc - 3));
                        RotationVector.Z = float.Parse(Lines[s + 2].Substring(ZLoc + 2, Lines[s + 2].Length - ZLoc - 3));

                        CurrentLevel.LevelStart = new Prop("LevelStart.txt", PositionVector, RotationVector);
                        CurrentLevel.Props.Add(CurrentLevel.LevelStart);
                    }
                    else if (Lines[s].StartsWith("LevelFinish = "))
                    {
                        Vector3 PositionVector = new Vector3();
                        int XLoc, YLoc, ZLoc;
                        XLoc = Lines[s + 1].IndexOf('X');
                        YLoc = Lines[s + 1].IndexOf('Y');
                        ZLoc = Lines[s + 1].IndexOf('Z');
                        PositionVector.X = float.Parse(Lines[s + 1].Substring(XLoc + 2, YLoc - XLoc - 3));
                        PositionVector.Y = float.Parse(Lines[s + 1].Substring(YLoc + 2, ZLoc - YLoc - 3));
                        PositionVector.Z = float.Parse(Lines[s + 1].Substring(ZLoc + 2, Lines[s + 1].Length - ZLoc - 3));

                        Vector3 RotationVector = new Vector3();
                        XLoc = Lines[s + 2].IndexOf('X');
                        YLoc = Lines[s + 2].IndexOf('Y');
                        ZLoc = Lines[s + 2].IndexOf('Z');
                        RotationVector.X = float.Parse(Lines[s + 2].Substring(XLoc + 2, YLoc - XLoc - 3));
                        RotationVector.Y = float.Parse(Lines[s + 2].Substring(YLoc + 2, ZLoc - YLoc - 3));
                        RotationVector.Z = float.Parse(Lines[s + 2].Substring(ZLoc + 2, Lines[s + 2].Length - ZLoc - 3));

                        CurrentLevel.LevelFinish = new Prop("LevelFinish.txt", PositionVector, RotationVector);
                        CurrentLevel.Props.Add(CurrentLevel.LevelFinish);
                    }
                    else if (Lines[s].StartsWith("Props = "))
                    {
                        List<Prop> Props = new List<Prop>();
                        int NumOfProps = int.Parse(Lines[s].Substring("Props = ".Length));
                        for (int p = 0; p < NumOfProps; p++)
                        {
                            Prop P;
                            string PropName = Lines[s + 3 * p + 1];
                            P = new Prop(PropName);

                            Vector3 ReadVector = new Vector3();
                            int XLoc, YLoc, ZLoc;
                            XLoc = Lines[s + 3 * p + 2].IndexOf('X');
                            YLoc = Lines[s + 3 * p + 2].IndexOf('Y');
                            ZLoc = Lines[s + 3 * p + 2].IndexOf('Z');
                            ReadVector.X = float.Parse(Lines[s + 3 * p + 2].Substring(XLoc + 2, YLoc - XLoc - 3));
                            ReadVector.Y = float.Parse(Lines[s + 3 * p + 2].Substring(YLoc + 2, ZLoc - YLoc - 3));
                            ReadVector.Z = float.Parse(Lines[s + 3 * p + 2].Substring(ZLoc + 2, Lines[s + 3 * p + 2].Length - ZLoc - 3));
                            P.SetPosition(ReadVector);

                            XLoc = Lines[s + 3 * p + 3].IndexOf('X');
                            YLoc = Lines[s + 3 * p + 3].IndexOf('Y');
                            ZLoc = Lines[s + 3 * p + 3].IndexOf('Z');
                            ReadVector.X = float.Parse(Lines[s + 3 * p + 3].Substring(XLoc + 2, YLoc - XLoc - 3));
                            ReadVector.Y = float.Parse(Lines[s + 3 * p + 3].Substring(YLoc + 2, ZLoc - YLoc - 3));
                            ReadVector.Z = float.Parse(Lines[s + 3 * p + 3].Substring(ZLoc + 2, Lines[s + 3 * p + 3].Length - ZLoc - 3));
                            P.SetRotation(ReadVector);

                            Props.Add(P);
                        }
                        CurrentLevel.Props = Props;
                        s += 3 * NumOfProps;
                    }
                }
            }
        }

        public void SaveLevelData(StorageDevice Device, string FileName)
        {
            // Open a storage container.
            StorageContainer Container = Device.OpenContainer("Levels");

            string CodeFileName = "Code" + FileName;
            // Get the path of the save game.
            FileName = "Levels/" + FileName;
            CodeFileName = "Levels/" + CodeFileName;
            //FileName = Path.Combine(Container.Path, FileName);
            //CodeFileName = Path.Combine(Container.Path, CodeFileName);

            try
            {
                StreamWriter SW = new StreamWriter(FileName);
                StreamWriter CSW = new StreamWriter(CodeFileName);

                SW.WriteLine("LevelMusic = " + CurrentLevel.LevelMusic);

                SW.WriteLine("LevelStart = ");
                SW.WriteLine(CurrentLevel.LevelStart.Position);
                SW.WriteLine(CurrentLevel.LevelStart.Rotation);

                SW.WriteLine("LevelFinish = ");
                SW.WriteLine(CurrentLevel.LevelFinish.Position);
                SW.WriteLine(CurrentLevel.LevelFinish.Rotation);

                SW.WriteLine("Props = " + CurrentLevel.Props.Count);
                for (int p = 0; p < CurrentLevel.Props.Count; p++)
                {
                    SW.WriteLine(CurrentLevel.Props[p].Name);
                    SW.WriteLine(CurrentLevel.Props[p].Position);
                    SW.WriteLine(CurrentLevel.Props[p].Rotation);

                    string Type = "new " + CurrentLevel.Props[p].Name.Remove(CurrentLevel.Props[p].Name.Length - 4);
                    string Position = "new Vector3(" + CurrentLevel.Props[p].Position.X + "f, " + CurrentLevel.Props[p].Position.Y + "f, " + CurrentLevel.Props[p].Position.Z + "f)";
                    string Rotation = "new Vector3(" + CurrentLevel.Props[p].Rotation.X + "f, " + CurrentLevel.Props[p].Rotation.Y + "f, " + CurrentLevel.Props[p].Rotation.Z + "f)";
                    CSW.WriteLine("Props.Add(" + Type + "(" + Position + ", " + Rotation + "));");
                }

                SW.Close();
                CSW.Close();
            }
            catch (Exception E)
            {
                Console.WriteLine(E);
                //Stream Writer messed up.
            }

            // Dispose the container, to commit changes.
            Container.Dispose();
        }

        public bool MouseOverName()
        {
            float MX = Mouse.GetState().X;
            float MY = Mouse.GetState().Y;
            if (MX < 200 && MX > 10 && MY < 30 && MY > 10)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}
